#include "ObjectPtr.h"
#include "Class.h"

#include "GenericInfo.h"
#include "PlaceholderType.h"

#include "Object/Object.h"

#include "TypeInstance.h"
#include "GarbageCollectable.h"

CString CSharedObjectPtrType::PlaceholderName = U"T";

CSharedObjectPtrType::CSharedObjectPtrType()
    :CType(STypeID(U"TSharedObjectPtr",true)
        ,ETypeFlags::Generic | ETypeFlags::SharedObjectPtr
        )
{
    GenericInfo = new CGenericInfo();

    auto PlaceholderType = new CPlaceholderType(
        STypeID(GetFullName()+U"::"+PlaceholderName,true)
        ,ETypeFlags::None
        , 0
        ,this
        );
    GenericInfo->PlaceholderTypes.Add(PlaceholderType);
}

CSharedObjectPtrType *CSharedObjectPtrType::GetStaticType()
{
    static CSharedObjectPtrType *StaticType = new CSharedObjectPtrType();
    return StaticType;
}


CString CSharedObjectPtrType::CreateTypeName(const CString &ElementType)
{
    return STypeID::CreateTemplateTypeName(
        GetStaticType()->GetFullName()
        ,{ElementType}
        ,{PlaceholderName}
        );
}

STypeID CSharedObjectPtrType::CreateID(const SDecoratedType &ElementType)
{
    return CreateID(ElementType.BasicType->ID);
}

STypeID CSharedObjectPtrType::CreateID(const STypeID &ClassType)
{
    return STypeID(
        GetStaticType()->ID
        ,{ClassType}
        ,{PlaceholderName}
        );
}

static uint8_t RegisterObjectPtr=(CSharedObjectPtrType::GetStaticType(),0); 

class CSharedObjectPtrInstance : public ITypeInstance
{
protected:
    CObjectPtrTypeGeneralized * Type;
public:
    CSharedObjectPtrInstance(CObjectPtrTypeGeneralized * InType)
        :Type(InType)
    {
    }

    bool IsForceConvertableTo(CType * Other) override
    {

        if(!Other->IsSharedObjectPtrType())
        {
            return false;
        }

        return Type->GetObjectClass()->CanObjectConvertTo( 
        reinterpret_cast<CObjectPtrTypeGeneralized*>(Other)->GetObjectClass());
    }

    virtual uint32_t GetSize() override
    {
        return sizeof(TSharedObjectPtr<RObject>);
    }

    virtual void InitInstance(void * Memory) override
    {
        new (Memory) TSharedObjectPtr<RObject>();
    }

    virtual void EraseInstance(void * Instance) override
    {
        ((TSharedObjectPtr<RObject> *)Instance)->Clear();
    }

    virtual void CloneInstance(const void * Instance, void * TargetMemory) override
    {
        new (TargetMemory) TSharedObjectPtr<RObject>(*(const TSharedObjectPtr<RObject> *)Instance);
    }

    virtual uint64_t GetInstanceHash(const void * InInstance) const override
    {
        return ((const TSharedObjectPtr<RObject> *)InInstance)->GetHashCode();
    }

    virtual bool IsInstanceEqual(const void * A,const void * B) const override
    {
        return *((const TSharedObjectPtr<RObject> *)A)==*((const TSharedObjectPtr<RObject> *)B);
    }

    virtual void * CreateInstance() override
    {
        return new TSharedObjectPtr<RObject>();
    }

    virtual void ResetAllocated(void * Memory) override
    {
        //clear old value
        TSharedObjectPtr<RObject> *OldValue=reinterpret_cast<TSharedObjectPtr<RObject>*>(Memory);
        OldValue->Clear();
    }

    virtual CString ToDebugString(const void * InInstance) const override
    {
        auto ObjectPtr=reinterpret_cast<const TSharedObjectPtr<RObject>*>(InInstance);
        if(!ObjectPtr->IsValid())
        {
            return U"nullptr";
        }

        return Type->GetObjectClass()->ToDebugString(InInstance);
    }

    virtual int64_t GetDebugNamedChildNum(const void * Instance) const override
    { 
        return Type->GetObjectClass()->Instancable->GetDebugNamedChildNum(Instance);
    }

    virtual CString GetDebugNamedChildName(int64_t Index,const void * Instance) const override
    { 
        return Type->GetObjectClass()->Instancable->GetDebugNamedChildName(Index,Instance);
    }

    virtual CType* GetDebugNamedChildType(int64_t Index,const void * Instance) const override
    { 
        return Type->GetObjectClass()->Instancable->GetDebugNamedChildType(Index,Instance);
    }
    
    virtual const void* GetDebugNamedChildValue(int64_t Index,const void * Instance) const override
    { 
        return Type->GetObjectClass()->Instancable->GetDebugNamedChildValue(Index,Instance);
    }


};


class CSharedObjectPtrGC:public IGarbageCollectable
{
protected:
    CObjectPtrTypeGeneralized * Type;
public:
    CSharedObjectPtrGC(CObjectPtrTypeGeneralized* InType)
        :Type(InType)
    {
    }

    //收集指定对象引用的对象,如果引用了多次，就会重复添加
    virtual void CollectReferencedObjects(
        void* InInstance,
        TForwardForwardLinkedList<RObject*>& OutReferencedObjects
    ) override
    {
        auto ObjectPtr = reinterpret_cast<TSharedObjectPtr<RObject>*>(InInstance);
        if(ObjectPtr->IsValid())
        {
            OutReferencedObjects.InsertAtHead(ObjectPtr->Get());
        }
    }

};


CObjectPtrTypeGeneralized::
    CObjectPtrTypeGeneralized(CClass *InClass)
   :CType(
    CSharedObjectPtrType::CreateID(InClass)
   ,InClass->IsScriptType() ? ETypeFlags::SharedObjectPtr | ETypeFlags::Generalized | ETypeFlags::Script
                            : ETypeFlags::SharedObjectPtr | ETypeFlags::Generalized
   )
{
    Instancable=new CSharedObjectPtrInstance(this);

    GeneralizedInfo=new CGeneralizedInfo();
    GeneralizedInfo->TemplateType=CSharedObjectPtrType::GetStaticType();
    GeneralizedInfo->TypeArguments.Add(InClass);

    GeneralizedInfo->TemplateType->GenericInfo->GeneralizedTypes.Add(this);
    reinterpret_cast<CGenericInfo*>(GeneralizedInfo->TemplateType->GenericInfo)->GenericMap.Add(TVector<CType*>({InClass}),this);
    
    //垃圾回收接口
    GarbageCollectable=new CSharedObjectPtrGC(this);
    
}

CObjectPtrTypeGeneralized *CObjectPtrTypeGeneralized::FindClassSharedObjectPtrType(CClass *InClass)
{
    CGenericInfo* GenericInfo = reinterpret_cast<CGenericInfo*>(CSharedObjectPtrType::GetStaticType()->GenericInfo);
    auto SharedPtrType = reinterpret_cast<CGenericInfo*>(CSharedObjectPtrType::GetStaticType()->GenericInfo)->GenericMap.Find({InClass});
    if(SharedPtrType)
    {
        CObjectPtrTypeGeneralized * Result = reinterpret_cast<CObjectPtrTypeGeneralized*>(SharedPtrType);
        return Result;
    }
    else
    {
        return nullptr;
    }
}

CClass *CObjectPtrTypeGeneralized::GetObjectClass() const
{
    return reinterpret_cast<CClass*>(GeneralizedInfo->TypeArguments[0]);
}

void CObjectPtrTypeGeneralized::SetObjectPtr(void *Instance, RObject *InObject)
{
    TSharedObjectPtr<RObject> *Value=reinterpret_cast<TSharedObjectPtr<RObject>*>(Instance);
    *Value=InObject;
}

RObject *CObjectPtrTypeGeneralized::GetObjectPtr(void *Instance)
{
    TSharedObjectPtr<RObject> *Value=reinterpret_cast<TSharedObjectPtr<RObject>*>(Instance);
    return Value->Get();
}

// CString CObjectPtrTypeGeneralized::CreateTypeName(CClass *InClass)
// {
//     return CreateTypeName(InClass->GetFullName());
// }

// CString CObjectPtrTypeGeneralized::CreateTypeName(const CString &InClassName)
// {
//     return CSharedObjectPtrType::GetStaticType()->GetFullName()+ U"<"+ InClassName +U">";
// }
